#include <map>
#include <string>
#include <stdio.h>
#include <typeinfo>

/*
 * type here is used to distinguish those dispatchers who have
 * same K and same Fn signature, but have different usages, so
 * we can make different singleton instances by different types
 */
template<int type, typename K, typename Fn>
class callback_dispatcher;

template<int type, typename K, typename R, typename... Args>
class callback_dispatcher<type, K, R(Args...)> {
public:
    typedef R (*fn_handler)(Args...);

    callback_dispatcher() {
        printf("constructor\n");
    }
    ~callback_dispatcher() {
        printf("deconstructor\n");
    }

    static callback_dispatcher& get() {
        static callback_dispatcher instance;
        printf("\n--%s--\n", typeid(instance).name());
        return instance;
    }

    bool has_handler(const K& key) const {
        return handlers_.find(key) != handlers_.end();
    }

    void register_handler(const K& key, fn_handler handler) {
        if (handlers_.find(key) != handlers_.end())  {
            printf("already has handle %s\n", std::to_string(key).c_str());
            return;
        }

        handlers_[key] = handler;
        for (const auto& handle : handlers_) {
            printf("has handle %s\n", std::to_string(handle.first).c_str());
        }
    }

    R dispatch(const K& key, Args... args) {
        auto it = handlers_.find(key);
        if (it == handlers_.end()) {
            std::string str = std::to_string(key);
            printf("handle not found: %s", str.c_str());

            return R();
        }

        return it->second(args...);
    }

private:
    std::map<K, fn_handler> handlers_;
};

template<int type, typename K, typename... Args>
class callback_dispatcher<type, K, void(Args...)> {
public:
    typedef void (*fn_handler)(Args...);

    callback_dispatcher() {
        printf("constructor\n");
    }
    ~callback_dispatcher() {
        printf("deconstructor\n");
    }

    static callback_dispatcher& get() {
        static callback_dispatcher instance;
        return instance;
    }

    bool has_handler(const K& key) const {
        return handlers_.find(key) != handlers_.end();
    }

    void register_handler(const K& key, fn_handler handler) {
        if (handlers_.find(key) != handlers_.end())  {
            printf("already has handle %s\n", std::to_string(key).c_str());
            return;
        }

        handlers_[key] = handler;
        for (const auto& handle : handlers_) {
            printf("has handle %s\n", std::to_string(handle.first).c_srt());
        }
    }

    void dispatch(const K& key, Args... args) {
        auto it = handlers_.find(key);
        if (it == handlers_.end()) {
            std::string str = std::to_string(key);
            printf("handle not found: %s\n", str.c_str());

            return;
        }

        return it->second(args...);
    }

private:
    std::map<K, fn_handler> handlers_;
};

typedef callback_dispatcher<0, int, int(const std::string&, const std::string&)> message_dispatcher ;

int xx(const std::string& a, const std::string& b) {
    printf("a: %s b: %s\n", a.c_str(), b.c_str());
    return 0;
}

template<typename T>
class test {
public:
    static test& get() {
        static test xx;
        return xx;
    }
};

int main(int argc, const char **argv) {
    {
    auto& dis = message_dispatcher::get();
    if (dis.has_handler(1)) {
        printf("has handle 1 \n");
        return -1;
    }
    dis.register_handler(1, xx);
    }

    {
    auto& dis = message_dispatcher::get();
    if (dis.has_handler(3)) {
        printf("has handle 3 \n");
        return -1;
    }
    dis.register_handler(3, xx);

    }

    {
    auto& dis = message_dispatcher::get();
    if (dis.has_handler(2)) {
        printf("has handle 2 \n");
        return -1;
    }
    dis.register_handler(2, xx);

    }


    auto& dis = message_dispatcher::get();
    dis.dispatch(1, "1", "1");
    dis.dispatch(2, "2", "2");
    dis.dispatch(3, "3", "3");
    auto& dis1 = message_dispatcher::get();
    auto& dis2 = message_dispatcher::get();
    auto& dis3 = message_dispatcher::get();
    printf("\n%s \n%s \n%s\n", typeid(dis1).name(), typeid(dis2).name(), typeid(dis3).name());
    printf("%p %p %p \n", (void *)&dis1, (void *)&dis2, (void *)&dis3);

    printf("%p %p %p \n", (void *)&test<int>::get(), (void *)&test<int>::get(), (void *)&test<int>::get());
    return 0;
}

